/* License Notice:
**
** This program is free software: you can redistribute it and/or modify
**    it under the terms of the GNU General Public License as published by
**    the Free Software Foundation, either version 3 of the License, or
**    (at your option) any later version.
** This program is distributed in the hope that it will be useful,
**   but WITHOUT ANY WARRANTY; without even the implied warranty of
**   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
**   GNU General Public License for more details.
** You should have received a copy of the GNU General Public License
**   along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

/**
 * @file demo_character.cpp
 * @author TooOld2Rock'nRoll
 * @see demo_character.hpp
 */
/*---- Includes ----*/
#include "demo_character.hpp"

#include "global.hpp"
#include "toolbox/debug.h"


/*---- Global Variables ----*/


/*---- Methods Implementation ----*/
DemoCharacter::DemoCharacter (DomainID_t domain_id, Shader *sh) :
                        Character (domain_id, sh, DemoCharacter::_anime_max, DemoCharacter::_audio_effect_max)
{
    _debug (TRACE, "::%s (DomID=%u)", __FUNCTION__, domain_id);

    this->u_current_state = extended_character_state_e::_extend_state_max;
    this->v_states_list.resize (extended_character_state_e::_extend_state_max);
    this->_populateDefaultStatesList ();

    this->v_command_list.resize (extended_character_commands_e::_extend_command_max);
    this->_populateDefaultCommandList ();

    this->_populateDefaultChainActions ();

    //setup character base statistics_s
    ///@todo This should not be hardcoded, use like this until we have character sheets.
    this->_statistics.u_walking_speed = 60;
    this->_statistics.u_jumping_speed = 500;
    this->_statistics.f_chain_action_time_out = 0.33;
}//End Constructor


void DemoCharacter::load ()
{
    Texture *fighter_sprite_sheet = nullptr;
    Sprite2D *slist = nullptr;
    Audio::Effect *alist = nullptr;

    _debug (TRACE, "::%s ()", __FUNCTION__);

    //setup texture and sprits
    fighter_sprite_sheet = this->p_resource_manager->getTexture (this->_res_domain_id, "res/img/players/template_character/template_character.png");
    if (!fighter_sprite_sheet->getTileMap ()) //check if texture was already mapped
        fighter_sprite_sheet->createTileMap (10, 11);

    this->setTexture (fighter_sprite_sheet);
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); //to keep the template image pixelated
    this->setAnimationFPS (12, false);
    this->setScaleFactor (5.f);

    //setup animations
    slist = this->getSprites ();
    slist[animation_list_e::LOADING].addContiguousTxTilesToList (20, 27); //using the running sequence
    slist[animation_list_e::LOADING].setAnimationFPS (12, true);
    slist[animation_list_e::OPENING_ANIME].addTxTileToList (0);
    //slist[animation_list::WIN].addContiguousTxTilesToList (0, 0);
    slist[animation_list_e::LOSE].addContiguousTxTilesToList (90, 99);
    slist[animation_list_e::IDLE].addContiguousTxTilesToList (0, 9);
    slist[animation_list_e::IDLE].setAnimationFPS (12, true);
    slist[animation_list_e::WALKING].addContiguousTxTilesToList (10, 17);
    slist[animation_list_e::WALKING].setAnimationFPS (12, true);
    slist[animation_list_e::RUNNING].addContiguousTxTilesToList (20, 27);
    slist[animation_list_e::RUNNING].setAnimationFPS (12, true);
    //slist[animation_list::DASHING].addContiguousTxTilesToList (0, 0);
    slist[animation_list_e::ROLLING].addContiguousTxTilesToList (73, 79);
    slist[animation_list_e::CROUCH_IDLE].addContiguousTxTilesToList (30, 39);
    slist[animation_list_e::CROUCH_IDLE].setAnimationFPS (12, true);
    slist[animation_list_e::CROUCH_WALKING].addContiguousTxTilesToList (40, 49);
    slist[animation_list_e::CROUCH_WALKING].setAnimationFPS (12, true);
    slist[animation_list_e::JUMP].addContiguousTxTilesToList (70, 72);
    slist[animation_list_e::JUMP].setAnimationFPS (0);
    slist[animation_list_e::AIR_SPIN].addContiguousTxTilesToList (54, 59);
    slist[animation_list_e::LAND].addContiguousTxTilesToList (60, 68);
    slist[animation_list_e::LAND].setAnimationFPS (18, false);
    slist[animation_list_e::BLOCK].addContiguousTxTilesToList (80, 87);
    slist[animation_list_e::BLOCK].setAnimationFPS (12, true);
    slist[animation_list_e::JAB].addContiguousTxTilesToList (100, 103);
    slist[animation_list_e::CROSS].addContiguousTxTilesToList (104, 108);

    //load audio effects
    alist = this->getAudioEffects ();
    alist[audio_effect_list_e::SOUND_LIGHT_PUNCH].setSource (this->p_resource_manager->getSoundEffect (this->_res_domain_id,
                                                                                        "res/audio/FilmCowRecordedSFX/woosh 23.wav"));
    alist[audio_effect_list_e::SOUND_HARD_PUNCH].setSource (this->p_resource_manager->getSoundEffect (this->_res_domain_id,
                                                                                        "res/audio/FilmCowRecordedSFX/woosh 25.wav"));
    alist[audio_effect_list_e::SOUND_JUMP].setSource (this->p_resource_manager->getSoundEffect (this->_res_domain_id,
                                                                                        "res/audio/FilmCowRecordedSFX/woosh 19.wav"));
    alist[audio_effect_list_e::SOUND_LAND].setSource (this->p_resource_manager->getSoundEffect (this->_res_domain_id,
                                                                                        "res/audio/FilmCowRecordedSFX/body fall 4.wav"));
}//End load ()


/**
 * @brief Loads the most basic commands available for a character.
 *
 * Most Commands used by a Character will implement the default behavior, loading this list and than changing
 *    specific ones may be advisable.
 */
void DemoCharacter::_populateDefaultCommandList ()
{
    static EmptyCommand<Character> command_empty; ///<@todo: this will be unnecessary once the list is completed...

    static WalkCommand comm_walk;
    static RunCommand comm_run;
    static JumpCommand comm_jump;
    static ShortJumpCommand comm_short_jump;
    static LongJumpCommand comm_long_jump;
    static LightPunchCommand command_light_punch;
    static HardPunchCommand command_hard_punch;
    static CrouchWalkCommand comm_crouch_walk;


    _debug (TRACE, "::%s ()", __FUNCTION__);

    this->v_command_list[character_commands_t::WALK] = &comm_walk;
    this->v_command_list[character_commands_t::JUMP] = &comm_jump;
    this->v_command_list[extended_character_commands_e::LIGHT_PUNCH] = &command_light_punch;
    this->v_command_list[extended_character_commands_e::HARD_PUNCH] = &command_hard_punch;
    this->v_command_list[extended_character_commands_e::SHORT_JUMP] = &comm_short_jump;
    this->v_command_list[extended_character_commands_e::LONG_JUMP] = &comm_long_jump;
    this->v_command_list[extended_character_commands_e::CROUCH_WALK] = &comm_crouch_walk;
    this->v_command_list[extended_character_commands_e::RUN] = &comm_run;
}//End _populateDefaultCommandList ()


/**
 * @brief Loads the most basic state handling available for a character.
 *
 * Most States used by a Character will implement the default behavior, loading this list and than changing
 *    specific ones may be advisable.
 */
void DemoCharacter::_populateDefaultStatesList ()
{
    static EmptyState<Character> state_empty; ///<@todo: this will be unnecessary once the list is completed...

    static LoadingState state_loading;
    static IdleState state_idle;
    static WalkingState state_walking;
    static RunningState state_running;
    static CrouchState state_crouch;
    static JumpingState state_jumping;
    static LongJumpState state_long_jumping;
    static AttackState state_attacking;
    static BlockState state_block;
    static RollingState state_roll;

    static CrouchWalkingState state_crouch_walking;

    _debug (TRACE, "::%s ()", __FUNCTION__);

    this->v_states_list[character_state_t::LOADING] = &state_loading;
    this->v_states_list[character_state_t::OPENING_ANIME] = &state_empty;
    //this->v_states_list[character_state_t::WIN_ANIME] = &state_empty;
    //this->v_states_list[character_state_t::LOSE_ANIME] = &state_empty;
    //this->v_states_list[character_state_t::STOP] = &state_empty;
    this->v_states_list[character_state_t::IDLE] = &state_idle;
    this->v_states_list[character_state_t::WALKING] = &state_walking;
    //this->v_states_list[character_state_t::DASHING] = &state_empty;
    this->v_states_list[character_state_t::CROUCHING] = &state_crouch;
    this->v_states_list[character_state_t::JUMPING] = &state_jumping;
    this->v_states_list[character_state_t::BLOCKING] = &state_block;
    this->v_states_list[character_state_t::ATTACKING] = &state_attacking;

    this->v_states_list[extended_character_state_e::ROLLING] = &state_roll;
    // this->v_states_list[extended_character_state_e::DASHING] = &state_empty;
    this->v_states_list[extended_character_state_e::CROUCH_WALKING] = &state_crouch_walking;
    this->v_states_list[extended_character_state_e::RUNNING] = &state_running;
    this->v_states_list[extended_character_state_e::LONG_JUMPIMP] = &state_long_jumping;
}//End _populateDefaultStatesList ()


void DemoCharacter::_populateDefaultChainActions ()
{
    std::vector<controller_button_map_t> keys;
    std::vector<key_state> states;

    _debug (TRACE, "::%s ()", __FUNCTION__);

    //running left and right!
    keys = {controller_button_map_t::DPAD_RIGHT, controller_button_map_t::DPAD_RIGHT, controller_button_map_t::DPAD_RIGHT};
    states = {key_state::DOWN, key_state::UP, key_state::DOWN};
    this->mapChainAction (keys, states,
                          extended_character_state_e::RUNNING,
                          extended_character_commands_e::_extend_command_max);

    keys.clear ();
    keys = {controller_button_map_t::DPAD_LEFT, controller_button_map_t::DPAD_LEFT, controller_button_map_t::DPAD_LEFT};
    //states = same as before!
    this->mapChainAction (keys, states,
                          extended_character_state_e::RUNNING,
                          extended_character_commands_e::_extend_command_max);
}//end _populateDefaultChainActions


/*------------------------------------------------------------*/
/**
 * @brief Make the Character move considering its speed and time elapsed.
 *
 * @param puppet - reference to the Character.
 * @param delta_t - time elapsed since last game loop iteration.
 */
void WalkCommand::execute (DemoCharacter &puppet, double delta_t)
{
    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    if (puppet.facing() == DemoCharacter::facing_e::FACE_LEFT)
        puppet.getPosition().translate (-delta_t * puppet.getStatistics().u_walking_speed, 0);
    else
        puppet.getPosition().translate (delta_t * puppet.getStatistics().u_walking_speed, 0);
}//end LeftPressedCommand::execute ()

/**
 * @brief Make the Character move considering its speed and time elapsed.
 *
 * @param puppet - reference to the Character.
 * @param delta_t - time elapsed since last game loop iteration.
 */
void RunCommand::execute (DemoCharacter &puppet, double delta_t)
{
    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    if (puppet.facing() == DemoCharacter::facing_e::FACE_LEFT)
        puppet.getPosition().translate (-delta_t * puppet.getStatistics().u_walking_speed * 2.f, 0);
    else
        puppet.getPosition().translate (delta_t * puppet.getStatistics().u_walking_speed * 2.f, 0);
}//end LeftPressedCommand::execute ()

/**
 * @brief Changes the Character vertical speed based on its statistics_s.
 *
 * @param puppet - reference to the Character.
 * @param delta_t - time elapsed since last game loop iteration.
 */
void JumpCommand::execute (DemoCharacter &puppet, double delta_t)
{
    Character::statistics_s &statis = puppet.getStatistics ();

    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    statis.f_current_vertical_speed = statis.u_jumping_speed;
    puppet.getPosition().translate (0, -statis.f_current_vertical_speed * delta_t);
    puppet.playEffect (DemoCharacter::audio_effect_list_e::SOUND_JUMP);
}//end JumpCommand::execute ()

/**
 * @brief Changes the Character vertical and horizontal speed based on its statistics_s.
 *
 * @param puppet - reference to the Character.
 * @param delta_t - time elapsed since last game loop iteration.
 */
void ShortJumpCommand::execute (DemoCharacter &puppet, double delta_t)
{
    Character::statistics_s &statis = puppet.getStatistics ();

    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    statis.f_current_vertical_speed = statis.u_jumping_speed;
    statis.f_current_horizontal_speed = 1.5 * (puppet.facing() == Character::facing_e::FACE_LEFT ?
                                                                -(float)statis.u_walking_speed : statis.u_walking_speed);

    puppet.getPosition().translate (0, -statis.f_current_vertical_speed * delta_t);
    puppet.getPosition().translate (statis.f_current_horizontal_speed * delta_t, 0);

    puppet.playEffect (DemoCharacter::audio_effect_list_e::SOUND_JUMP);
}//end ShortJumpCommand::execute ()

/**
 * @brief Changes the Character vertical and horizontal speed based on its statistics_s.
 *
 * @param puppet - reference to the Character.
 * @param delta_t - time elapsed since last game loop iteration.
 */
void LongJumpCommand::execute (DemoCharacter &puppet, double delta_t)
{
    Character::statistics_s &statis = puppet.getStatistics ();

    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    statis.f_current_vertical_speed = statis.u_jumping_speed;
    statis.f_current_horizontal_speed = 2 * (puppet.facing() == Character::facing_e::FACE_LEFT ?
                                                                -(float)statis.u_walking_speed : statis.u_walking_speed);

    puppet.getPosition().translate (0, -statis.f_current_vertical_speed * delta_t);
    puppet.getPosition().translate (statis.f_current_horizontal_speed * delta_t, 0);

    puppet.playEffect (DemoCharacter::audio_effect_list_e::SOUND_JUMP);
}//end LongJumpCommand::execute ()

/** @brief Original Attack state can't handle different attacks, set a light punch for the attack state! */
void LightPunchCommand::execute (DemoCharacter &puppet, double delta_t)
{
    _debug (TRACE, "::%s ()", __FUNCTION__);

    puppet.setState (character_state_t::ATTACKING);
    puppet.setActiveSprite (DemoCharacter::animation_list_e::JAB);
    puppet.playEffect (DemoCharacter::audio_effect_list_e::SOUND_LIGHT_PUNCH);
}//end execute ()

/** @brief Original Attack state can't handle different attacks, set a hard punch for the attack state! */
void HardPunchCommand::execute (DemoCharacter &puppet, double delta_t)
{
    _debug (TRACE, "::%s ()", __FUNCTION__);

    puppet.setState (character_state_t::ATTACKING);
    puppet.setActiveSprite (DemoCharacter::animation_list_e::CROSS);
    puppet.playEffect (DemoCharacter::audio_effect_list_e::SOUND_HARD_PUNCH);
}//end execute ()

/**
 * @brief Make the Character move considering its speed and time elapsed.
 *
 * @param puppet - reference to the Character.
 * @param delta_t - time elapsed since last game loop iteration.
 */
void CrouchWalkCommand::execute (DemoCharacter &puppet, double delta_t)
{
    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    if (puppet.facing() == DemoCharacter::facing_e::FACE_LEFT)
        puppet.getPosition().translate (-delta_t * puppet.getStatistics().u_walking_speed/2, 0);
    else
        puppet.getPosition().translate (delta_t * puppet.getStatistics().u_walking_speed/2, 0);
}//end CrouchWalkCommand::execute ()



/*------------------------------------------------------------*/

/** @brief Since Idle accepts any key as a command, we will map the entire input_map!  */
IdleState::IdleState () : AutomatedState(controller_button_map_t::_button_max)
{
    _debug (TRACE, "::%s ()", __FUNCTION__);

    this->a_valid_transitions[controller_button_map_t::DPAD_LEFT]._input = controller_button_map_t::DPAD_LEFT;
    this->a_valid_transitions[controller_button_map_t::DPAD_LEFT].b_state[key_state::DOWN] = true;
    this->a_valid_transitions[controller_button_map_t::DPAD_LEFT].b_state[key_state::HOLD] = true;
    this->a_valid_transitions[controller_button_map_t::DPAD_LEFT]._next_state = character_state_t::WALKING;
    this->a_valid_transitions[controller_button_map_t::DPAD_LEFT]._command_index = character_commands_t::WALK;

    this->a_valid_transitions[controller_button_map_t::DPAD_RIGHT]._input = controller_button_map_t::DPAD_RIGHT;
    this->a_valid_transitions[controller_button_map_t::DPAD_RIGHT].b_state[key_state::DOWN] = true;
    this->a_valid_transitions[controller_button_map_t::DPAD_RIGHT].b_state[key_state::HOLD] = true;
    this->a_valid_transitions[controller_button_map_t::DPAD_RIGHT]._next_state = character_state_t::WALKING;
    this->a_valid_transitions[controller_button_map_t::DPAD_RIGHT]._command_index = character_commands_t::WALK;

    this->a_valid_transitions[controller_button_map_t::DPAD_UP]._input = controller_button_map_t::DPAD_UP;
    this->a_valid_transitions[controller_button_map_t::DPAD_UP].b_state[key_state::DOWN] = true;
    this->a_valid_transitions[controller_button_map_t::DPAD_UP]._next_state = character_state_t::JUMPING;
    this->a_valid_transitions[controller_button_map_t::DPAD_UP]._command_index = character_commands_t::JUMP;

    this->a_valid_transitions[controller_button_map_t::DPAD_DOWN]._input = controller_button_map_t::DPAD_DOWN;
    this->a_valid_transitions[controller_button_map_t::DPAD_DOWN].b_state[key_state::DOWN] = true;
    this->a_valid_transitions[controller_button_map_t::DPAD_DOWN].b_state[key_state::HOLD] = true;
    this->a_valid_transitions[controller_button_map_t::DPAD_DOWN]._next_state = character_state_t::CROUCHING;
    this->a_valid_transitions[controller_button_map_t::DPAD_DOWN]._command_index = extended_character_commands_e::_extend_command_max;

    this->a_valid_transitions[controller_button_map_t::ACTION_A]._input = controller_button_map_t::ACTION_A;
    this->a_valid_transitions[controller_button_map_t::ACTION_A].b_state[key_state::DOWN] = true;
    this->a_valid_transitions[controller_button_map_t::ACTION_A]._next_state = character_state_t::ATTACKING;
    this->a_valid_transitions[controller_button_map_t::ACTION_A]._command_index = extended_character_commands_e::LIGHT_PUNCH;

    this->a_valid_transitions[controller_button_map_t::ACTION_B]._input = controller_button_map_t::ACTION_B;
    this->a_valid_transitions[controller_button_map_t::ACTION_B].b_state[key_state::DOWN] = true;
    this->a_valid_transitions[controller_button_map_t::ACTION_B]._next_state = character_state_t::ATTACKING;
    this->a_valid_transitions[controller_button_map_t::ACTION_B]._command_index = extended_character_commands_e::HARD_PUNCH;

    this->a_valid_transitions[controller_button_map_t::ACTION_Y]._input = controller_button_map_t::ACTION_Y;
    this->a_valid_transitions[controller_button_map_t::ACTION_Y].b_state[key_state::DOWN] = true;
    this->a_valid_transitions[controller_button_map_t::ACTION_Y]._next_state = character_state_t::BLOCKING;
    this->a_valid_transitions[controller_button_map_t::ACTION_Y]._command_index = extended_character_commands_e::_extend_command_max;
}//End Constructor

/** @brief Checks if the state transition is valid by searching the transition list for a match.
 * @param puppet - reference to the Character.
 * @param new_state - new state to get to.
 * @return <b>true</b> if new state exists, <b>false</b> if state is not predicted.
 */
bool IdleState::checkStateChange (DemoCharacter &puppet, unsigned new_state) const
{
    bool confirm = AutomatedState<DemoCharacter>::checkStateChange (puppet, new_state);

    //special cases not covered by the automated rules
    if (!confirm)
    {
        switch (new_state)
        {
            case extended_character_state_e::RUNNING:
                confirm = true;
                break;
            //end case extended_character_state_e::RUNNING:

            default:
                break;
        }//end switch (new_state)
    }//end if (!confirm)

    return confirm;
}//End checkStateChange ()

/** @brief Handle specific DemoCharacter input and pass over to Character if irrelevant. */
void IdleState::handleInput (DemoCharacter &puppet, controller_button_map_t input, key_state input_st, double delta_t)
{
    _debug (VERBOSE, "::%s (input='%s')", __FUNCTION__, sa_input_map_names[input]);

    //since all keys are used in the idle state, we can just jump to the index without searching...
    if (this->a_valid_transitions[input].b_state[input_st])
        if (puppet.setState (this->a_valid_transitions[input]._next_state))
            puppet.execute (this->a_valid_transitions[input]._command_index, delta_t);
}//end handleInput ()

/** @brief It will force the puppet to look at the target and update the state of the current active sprite. */
void IdleState::update (DemoCharacter &puppet, double delta_t)
{
    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    puppet.faceTarget ();

    //update animation frame
    puppet.getActiveSprite().update (delta_t);
}//end update ()


void WalkingState::enter (DemoCharacter &puppet)
{
    _debug (TRACE, "::%s ()", __FUNCTION__);

    puppet.setActiveSprite (DemoCharacter::animation_list_e::WALKING);
    EventManager::instance().push ((game_event_id_e)EV_CHARACTER_WALK_STEP, puppet.getPosition().get ());
}//end enter ()

/** @brief Regulates the possible state changes from Walking. */
bool WalkingState ::checkStateChange (DemoCharacter &puppet, unsigned new_state) const
{
    _debug (TRACE, "::%s (new_state=%u)", __FUNCTION__, new_state);

    switch (new_state)
    {
        case character_state_t::IDLE:
        case character_state_t::JUMPING:
            return true;
        //end case character_state_t::IDLE: ...

        case extended_character_state_e::CROUCH_WALKING:
            return true;
        //end case extended_character_state_e::CROUCH_WALKING:

        default:
            return false;
    }//end switch (new_satate)
}//end checkStateChange ())

/** @brief Walk left and right without changing state. */
void WalkingState::handleInput (DemoCharacter &puppet, controller_button_map_t input, key_state input_st, double delta_t)
{
    _debug (VERBOSE, "::%s (input='%s')", __FUNCTION__, sa_input_map_names[input]);

    switch (input)
    {
        case controller_button_map_t::DPAD_LEFT:
            if (puppet.facing() != Character::facing_e::FACE_LEFT)
                puppet.faceTo (DemoCharacter::facing_e::FACE_LEFT);

            if (input_st == key_state::HOLD)
                puppet.execute (character_commands_t::WALK, delta_t);
            else if (input_st == key_state::UP)
                puppet.setState (character_state_t::IDLE);
            break;
        //end case controller_button_map_t::DPAD_LEFT:

        case controller_button_map_t::DPAD_RIGHT:
            if (puppet.facing() != Character::facing_e::FACE_RIGHT)
                puppet.faceTo (DemoCharacter::facing_e::FACE_RIGHT);

            if (input_st == key_state::HOLD)
                puppet.execute (character_commands_t::WALK, delta_t);
            else if (input_st == key_state::UP)
                puppet.setState (character_state_t::IDLE);
            break;
        //end case controller_button_map_t::DPAD_RIGHT:

        case controller_button_map_t::DPAD_UP:
            if (input_st != key_state::DOWN)
                break; //we are only interested when up key is pressed
            else if (puppet.setState (character_state_t::JUMPING))
                puppet.execute (extended_character_commands_e::SHORT_JUMP, delta_t);

            break;
        //end case controller_button_map_t::DPAD_UP:

        case controller_button_map_t::DPAD_DOWN:
            if (input_st == key_state::UP)
                break;
            else if (puppet.setState (extended_character_state_e::CROUCH_WALKING))
                puppet.execute (extended_character_commands_e::CROUCH_WALK, delta_t);

            break;
        //end case controller_button_map_t::DPAD_DOWN:

        default:
            break;
    }//end switch (input)
}//end WalkingState::handleInput ()

/** @brief It will just update the state of the current active sprite. */
void WalkingState::update (DemoCharacter &puppet, double delta_t)
{
    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    //update animation frame
    puppet.getActiveSprite().update (delta_t);

    if (puppet.getActiveSprite().getFrameIndex()%3 == 0)
        EventManager::instance().push ((game_event_id_e)EV_CHARACTER_WALK_STEP, puppet.getPosition().get ());
}//end update ()


void RunningState::enter (DemoCharacter &puppet)
{
    _debug (TRACE, "::%s ()", __FUNCTION__);

    puppet.setActiveSprite (DemoCharacter::animation_list_e::RUNNING);
    EventManager::instance().push ((game_event_id_e)EV_CHARACTER_WALK_STEP, puppet.getPosition().get ());
}//end enter ()

/** @brief Regulates the possible state changes from Running. */
bool RunningState ::checkStateChange (DemoCharacter &puppet, unsigned new_state) const
{
    _debug (TRACE, "::%s (new_state=%u)", __FUNCTION__, new_state);

    switch (new_state)
    {
        case character_state_t::IDLE:
            return true;
        //end case character_state_t::IDLE: ...

        case extended_character_state_e::ROLLING:
        case extended_character_state_e::LONG_JUMPIMP:
            return true;
        //end case extended_character_state_e::ROLLING:

        default:
            return false;
    }//end switch (new_satate)
}//end checkStateChange ())

/** @brief Run left and right without changing state. */
void RunningState::handleInput (DemoCharacter &puppet, controller_button_map_t input, key_state input_st, double delta_t)
{
    _debug (VERBOSE, "::%s (input='%s')", __FUNCTION__, sa_input_map_names[input]);

    switch (input)
    {
        case controller_button_map_t::DPAD_LEFT:
            if (puppet.facing() != Character::facing_e::FACE_LEFT)
                puppet.faceTo (DemoCharacter::facing_e::FACE_LEFT);

            if (input_st == key_state::HOLD)
                puppet.execute (extended_character_commands_e::RUN, delta_t);
            else if (input_st == key_state::UP)
                puppet.setState (character_state_t::IDLE);
            break;
        //end case controller_button_map_t::DPAD_LEFT:

        case controller_button_map_t::DPAD_RIGHT:
            if (puppet.facing() != Character::facing_e::FACE_RIGHT)
                puppet.faceTo (DemoCharacter::facing_e::FACE_RIGHT);

            if (input_st == key_state::HOLD)
                puppet.execute (extended_character_commands_e::RUN, delta_t);
            else if (input_st == key_state::UP)
                puppet.setState (character_state_t::IDLE);
            break;
        //end case controller_button_map_t::DPAD_RIGHT:

        case controller_button_map_t::DPAD_UP:
            if (input_st != key_state::DOWN)
                break; //we are only interested when up key is pressed
            else if (puppet.setState (extended_character_state_e::LONG_JUMPIMP))
                puppet.execute (extended_character_commands_e::LONG_JUMP, delta_t);

            break;
        //end case controller_button_map_t::DPAD_UP:

        case controller_button_map_t::DPAD_DOWN:
            if (input_st == key_state::UP)
                break;
            else if (puppet.setState (extended_character_state_e::ROLLING))
                puppet.execute (extended_character_commands_e::RUN, delta_t);

            break;
        //end case controller_button_map_t::DPAD_DOWN:

        default:
            break;
    }//end switch (input)
}//end RunningState::handleInput ()

/** @brief It will just update the state of the current active sprite. */
void RunningState::update (DemoCharacter &puppet, double delta_t)
{
    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    //update animation frame
    puppet.getActiveSprite().update (delta_t);

    if (puppet.getActiveSprite().getFrameIndex()%3 == 0)
        EventManager::instance().push ((game_event_id_e)EV_CHARACTER_WALK_STEP, puppet.getPosition().get ());
}//end update ()


/** @brief It will only accept going back to idle. */
bool CrouchState::checkStateChange (DemoCharacter &puppet, unsigned new_state) const
{
    _debug (TRACE, "::%s (new_state=%u)", __FUNCTION__, new_state);

    return (new_state == character_state_t::IDLE ||
            new_state == extended_character_state_e::CROUCH_WALKING);
}//end checkStateChange ()

/** @brief Keeps crouching until key is released. */
void CrouchState::handleInput (DemoCharacter &puppet, controller_button_map_t input, key_state input_st, double delta_t)
{
    _debug (VERBOSE, "::%s (input='%s')", __FUNCTION__, sa_input_map_names[input]);

    switch (input)
    {
        case controller_button_map_t::DPAD_LEFT:
            if (input_st != key_state::DOWN)
                break;
            else if (puppet.setState (extended_character_state_e::CROUCH_WALKING))
            {
                puppet.faceTo (DemoCharacter::facing_e::FACE_LEFT);
                puppet.execute (extended_character_commands_e::CROUCH_WALK, delta_t);
            }//end else if (puppet.setState (extended_character_state_e::CROUCH_WALKING))

            break;
        //end case controller_button_map_t::DPAD_LEFT:

        case controller_button_map_t::DPAD_RIGHT:
            if (input_st != key_state::DOWN)
                break;
            else if (puppet.setState (extended_character_state_e::CROUCH_WALKING))
            {
                puppet.faceTo (DemoCharacter::facing_e::FACE_RIGHT);
                puppet.execute (extended_character_commands_e::CROUCH_WALK, delta_t);
            }//end else if (puppet.setState (extended_character_state_e::CROUCH_WALKING))

            break;
        //end case controller_button_map_t::DPAD_LEFT: ...

        case controller_button_map_t::DPAD_DOWN:
            if (input_st == key_state::UP)
                puppet.setState (character_state_t::IDLE);
            break;
        //end case controller_button_map_t::DPAD_DOWN:

        default:
            break;
    }//end switch (input)
}//end handleInput ()

/** @brief It will force the puppet to look at the target and update the state of the current active sprite. */
void CrouchState::update (DemoCharacter &puppet, double delta_t)
{
    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    puppet.faceTarget ();

    //update animation frame
    puppet.getActiveSprite().update (delta_t);
}//end update ()


/** @brief It will only accept going back to idle. */
bool CrouchWalkingState::checkStateChange (DemoCharacter &puppet, unsigned new_state) const
{
    _debug (TRACE, "::%s (new_state=%u)", __FUNCTION__, new_state);

    return (new_state == character_state_t::CROUCHING ||
            new_state == character_state_t::WALKING);
}//end checkStateChange ()

/** @brief Keeps crouch walking until key is released. */
void CrouchWalkingState::handleInput (DemoCharacter &puppet, controller_button_map_t input, key_state input_st, double delta_t)
{
    _debug (VERBOSE, "::%s (input='%s')", __FUNCTION__, sa_input_map_names[input]);

    switch (input)
    {
        case controller_button_map_t::DPAD_LEFT:
        case controller_button_map_t::DPAD_RIGHT:
            if (input_st == key_state::HOLD)
                puppet.execute (extended_character_commands_e::CROUCH_WALK, delta_t);
            else if (input_st == key_state::UP)
                puppet.setState (character_state_t::CROUCHING);

            break;
        //end case controller_button_map_t::DPAD_LEFT: ...

        case controller_button_map_t::DPAD_DOWN:
            if (input_st == key_state::UP)
                puppet.setState (character_state_t::WALKING);
            break;
        //end case controller_button_map_t::DPAD_DOWN:

        default:
            break;
    }//end switch (input)
}//end handleInput ()

/** @brief It will force the puppet to look at the target and update the state of the current active sprite. */
void CrouchWalkingState::update (DemoCharacter &puppet, double delta_t)
{
    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    //update animation frame
    puppet.getActiveSprite().update (delta_t);

    if (puppet.getActiveSprite().getFrameIndex () == 5)
        EventManager::instance().push ((game_event_id_e)EV_CHARACTER_WALK_STEP, puppet.getPosition().get ());
}//end update ()


/** @brief It will only accept going back to idle. */
bool JumpingState::checkStateChange (DemoCharacter &puppet, unsigned new_state) const
{
    _debug (TRACE, "::%s (new_state=%u)", __FUNCTION__, new_state);

    return (new_state == character_state_t::IDLE);
}//end checkStateChange ()

/** @brief Blocks all actions until puppet lands. */
void JumpingState::handleInput (DemoCharacter &puppet, controller_button_map_t input, key_state input_st, double delta_t)
{
    _debug (VERBOSE, "::%s (input='%s')", __FUNCTION__, sa_input_map_names[input]);

    switch (input)
    {
        //case controller_button_map_t::DPAD_DOWN:
            //if (input_st == key_state::UP)
                //puppet.setState (character_state_t::IDLE);
            //break;
        //end case controller_button_map_t::DPAD_DOWN:

        default:
            break;
    }//end switch (input)
}//end handleInput ()

/** @brief Changes jumping frames as parable evolves. */
void JumpingState::update (DemoCharacter &puppet, double delta_t)
{
    Character::statistics_s &statis = puppet.getStatistics ();

    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    statis.f_current_vertical_speed -= (GRAVITY_TEMP * delta_t);
    if (puppet.getPosition().get().y + puppet.getActiveSprite().getSize().getScaled().y < GROUND_LEVEL_TEMP)
    {
        puppet.getPosition().translate (0, -statis.f_current_vertical_speed * delta_t);
        puppet.getPosition().translate (statis.f_current_horizontal_speed * delta_t, 0);

        //update animation frame
        if (statis.f_current_vertical_speed <= statis.u_jumping_speed * -0.2)
            puppet.getActiveSprite().cropUsingSpTileMap (2, true);
        else if (statis.f_current_vertical_speed <= statis.u_jumping_speed * 0.5)
            puppet.getActiveSprite().cropUsingSpTileMap (1, true);
    }//end if (puppet.getPosition().y + puppet.getActiveSprite().getSize().y < GROUND_LEVEL_TEMP)
    else
    {
        if (puppet.getActiveSpriteIndex() == DemoCharacter::animation_list_e::LAND)
        {
            if (!puppet.getActiveSprite().update (delta_t))
                puppet.setState (character_state_t::IDLE);
        }//end if (puppet.getActiveSpriteIndex() == DemoCharacter::animation_list::LAND)
        else
        {
            statis.f_current_vertical_speed = 0;
            statis.f_current_horizontal_speed = 0;
            puppet.playEffect (DemoCharacter::audio_effect_list_e::SOUND_LAND);
            puppet.setActiveSprite (DemoCharacter::animation_list_e::LAND);
            puppet.getPosition().set (puppet.getPosition().get().x,
                                      GROUND_LEVEL_TEMP - puppet.getActiveSprite().getSize().getScaled().y);
            EventManager::instance().push ((game_event_id_e)EV_CHARACTER_LANDED, puppet.getPosition().get ());
        }//end else
    }//end else
}//end update ()


/** @brief It will only accept going back to idle. */
bool LongJumpState::checkStateChange (DemoCharacter &puppet, unsigned new_state) const
{
    _debug (TRACE, "::%s (new_state=%u)", __FUNCTION__, new_state);

    return (new_state == character_state_t::IDLE);
}//end checkStateChange ()

/** @brief Blocks all actions until puppet lands. */
void LongJumpState::handleInput (DemoCharacter &puppet, controller_button_map_t input, key_state input_st, double delta_t)
{
    _debug (VERBOSE, "::%s (input='%s')", __FUNCTION__, sa_input_map_names[input]);

    switch (input)
    {
        //case controller_button_map_t::DPAD_DOWN:
            //if (input_st == key_state::UP)
                //puppet.setState (character_state_t::IDLE);
            //break;
        //end case controller_button_map_t::DPAD_DOWN:

        default:
            break;
    }//end switch (input)
}//end handleInput ()

/** @brief Changes jumping frames as parable evolves. */
void LongJumpState::update (DemoCharacter &puppet, double delta_t)
{
    Character::statistics_s &statis = puppet.getStatistics ();

    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    statis.f_current_vertical_speed -= (GRAVITY_TEMP * delta_t);
    if (puppet.getPosition().get().y + puppet.getActiveSprite().getSize().getScaled().y < GROUND_LEVEL_TEMP)
    {
        puppet.getPosition().translate (0, -statis.f_current_vertical_speed * delta_t);
        puppet.getPosition().translate (statis.f_current_horizontal_speed * delta_t, 0);

        //update animation frame
        puppet.getActiveSprite().update (delta_t);
    }//end if (puppet.getPosition().y + puppet.getActiveSprite().getSize().y < GROUND_LEVEL_TEMP)
    else
    {
        if (puppet.getActiveSpriteIndex() == DemoCharacter::animation_list_e::LAND)
        {
            if (!puppet.getActiveSprite().update (delta_t))
                puppet.setState (character_state_t::IDLE);
        }//end if (puppet.getActiveSpriteIndex() == DemoCharacter::animation_list::LAND)
        else
        {
            statis.f_current_vertical_speed = 0;
            statis.f_current_horizontal_speed = 0;
            puppet.playEffect (DemoCharacter::audio_effect_list_e::SOUND_LAND);
            puppet.setActiveSprite (DemoCharacter::animation_list_e::LAND);
            puppet.getPosition().set (puppet.getPosition().get().x,
                                      GROUND_LEVEL_TEMP - puppet.getActiveSprite().getSize().getScaled().y);
            EventManager::instance().push ((game_event_id_e)EV_CHARACTER_LANDED, puppet.getPosition().get ());
        }//end else
    }//end else
}//end update ()


/** @brief It will only accept going back to idle. */
bool AttackState::checkStateChange (DemoCharacter &puppet, unsigned new_state) const
{
    _debug (TRACE, "::%s (new_state=%u)", __FUNCTION__, new_state);

    return (new_state == character_state_t::IDLE);
}//end checkStateChange ()

/** @brief Blocks all actions until attack ends. */
void AttackState::handleInput (DemoCharacter &puppet, controller_button_map_t input, key_state input_st, double delta_t)
{
    _debug (VERBOSE, "::%s (input='%s')", __FUNCTION__, sa_input_map_names[input]);

    switch (input)
    {
        //case controller_button_map_t::DPAD_DOWN:
            //if (input_st == key_state::UP)
                //puppet.setState (character_state_t::IDLE);
            //break;
        //end case controller_button_map_t::DPAD_DOWN:

        default:
            break;
    }//end switch (input)
}//end handleInput ()

/** @brief It will just update the state of the current active sprite. */
void AttackState::update (DemoCharacter &puppet, double delta_t)
{
    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    //update animation frame
    if (!puppet.getActiveSprite().update (delta_t))
        puppet.setState (character_state_t::IDLE);
}//end update ()


/** @brief It will only accept going back to idle. */
bool BlockState::checkStateChange (DemoCharacter &puppet, unsigned new_state) const
{
    _debug (TRACE, "::%s (new_state=%u)", __FUNCTION__, new_state);

    return (new_state == character_state_t::IDLE);
}//end checkStateChange ()

/** @brief Keeps crouching until key is released. */
void BlockState::handleInput (DemoCharacter &puppet, controller_button_map_t input, key_state input_st, double delta_t)
{
    _debug (VERBOSE, "::%s (input='%s')", __FUNCTION__, sa_input_map_names[input]);

    switch (input)
    {
        case controller_button_map_t::ACTION_Y:
            if (input_st == key_state::UP)
                puppet.setState (character_state_t::IDLE);
            break;
        //end case controller_button_map_t::DPAD_DOWN:

        default:
            break;
    }//end switch (input)
}//end handleInput ()

/** @brief Just update the animation, don't turn to face opponent. */
void BlockState::update (DemoCharacter &puppet, double delta_t)
{
    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    //update animation frame
    puppet.getActiveSprite().update (delta_t);
}//end update ()


void RollingState::enter (DemoCharacter &puppet)
{
    _debug (TRACE, "::%s ()", __FUNCTION__);

    puppet.setActiveSprite (DemoCharacter::animation_list_e::ROLLING);
    EventManager::instance().push ((game_event_id_e)EV_CHARACTER_LANDED, puppet.getPosition().get ());
}//end enter ()

/** @brief It will only accept going back to idle. */
bool RollingState::checkStateChange (DemoCharacter &puppet, unsigned new_state) const
{
    _debug (TRACE, "::%s (new_state=%u)", __FUNCTION__, new_state);

    return (new_state == character_state_t::IDLE);
}//end checkStateChange ()

/** @brief Blocks all actions until puppet lands. */
void RollingState::handleInput (DemoCharacter &puppet, controller_button_map_t input, key_state input_st, double delta_t)
{
    _debug (VERBOSE, "::%s (input='%s')", __FUNCTION__, sa_input_map_names[input]);

    switch (input)
    {
        //case controller_button_map_t::DPAD_DOWN:
            //if (input_st == key_state::UP)
                //puppet.setState (character_state_t::IDLE);
            //break;
        //end case controller_button_map_t::DPAD_DOWN:

        default:
            break;
    }//end switch (input)
}//end handleInput ()

/** @brief Changes jumping frames as parable evolves. */
void RollingState::update (DemoCharacter &puppet, double delta_t)
{
    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    //update animation frame
    if (puppet.getActiveSprite().update (delta_t))
        puppet.execute (extended_character_commands_e::RUN, delta_t);
    else
        puppet.setState (character_state_t::IDLE);
}//end update ()


